package com.example.mtblog.tool;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import jakarta.annotation.PostConstruct;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * redis 工具类
 **/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisUtils {
    private static RedisTemplate staticRedisTemplate;

    private final RedisTemplate redisTemplate ;

    public RedisUtils(RedisTemplate redisTemplate) {

        // 防止key中文乱码
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // key的序列化类型

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);


        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value的序列化类型
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();





        this.redisTemplate = redisTemplate;
    }

    @PostConstruct
    public void initRedis() {

        // 初始化设置 静态redis对象，方便后续操作数据
        staticRedisTemplate = redisTemplate;
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public static <T> void setCacheObject(final String key, final T value) {
        staticRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public static <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        staticRedisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    //  默认时间单位为秒
    public static <T> void setCacheObject(final String key, final T value, final Integer timeoutInSecond) {
        setCacheObject(key, value, timeoutInSecond, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public static boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功；false=设置失败
     */
    public static boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return Boolean.TRUE.equals(staticRedisTemplate.expire(key, timeout, unit));
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public static <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = staticRedisTemplate.opsForValue();
        return operation.get(key);
    }

    public static Integer getCacheNumber(final String key) {
        ValueOperations<String,Integer> operation = staticRedisTemplate.opsForValue();
        if(operation.get(key) == null)
            return  0 ;
          return operation.get(key);

    }

    public static Integer setCacheNumberAdd(final String key,final Integer addNum) {
        Integer number = 0 ;
        ValueOperations<String,Integer> operation = staticRedisTemplate.opsForValue();
        if(operation.get(key) == null)
            number =  0 ;
        number += addNum;
        setCacheObject(key,number);
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public static boolean deleteObject(final String key) {
        return Boolean.TRUE.equals(staticRedisTemplate.delete(key));
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public static List<String> scanKeysSimple( String pattern) {

         List<String> keys = (List<String>) staticRedisTemplate.execute(connection -> {
            RedisKeyCommands keyCmds = connection.keyCommands();
            ScanOptions scanOpts = ScanOptions.scanOptions().match(pattern + "*").count(1000).build();
            Cursor<byte[]> cursor = keyCmds.scan(scanOpts);
            Set<String> set = new HashSet<>();
            while (cursor.hasNext()) {
                byte[] bytes = cursor.next();
                set.add(new String(bytes, StandardCharsets.UTF_8));
            }
            return new ArrayList<>(set);
        }, true);

        return keys;
    }






}
